/************************************************************************
 *				SRLOS Team CopyRight
 *@file		task.h
 *@author	bloceanc
 *@date		7/17/2010
 *@note		task managerment
 ************************************************************************/
#include "commdef.h"
#include "task.h"
#include "task_sigle.h"
#include "task_config.h"


k_thread *g_current_thread;
k_process *g_current_process;
k_process *s_wait_terminate; // process need terminated.

// process list in system
k_process *s_queue_process;
k_mutex g_process_mutex;

// bottom of current thread kernel stack
unsigned int g_p_cur_thread_sp;



extern void mt_mutex_initset(k_mutex *mutex);
extern int mt_mutex_lock(k_mutex *mutex, long long time);
extern void mt_mutex_unlock(k_mutex *mutex);
extern void mt_mutex_overtime_handle(k_process *process);

extern void task_idmgr_init(void);

extern void comm_global_critical_section_enter(void);
extern void comm_global_critical_section_leave(void);

extern void task_thread_terminate_handler(k_process *process);
extern void task_process_terminate_handler(void);
extern void task_insert_process(k_process *process);
extern void task_thread_insert(k_thread *thread, unsigned int state);
extern void task_procomm_share_init(void);

extern k_thread *task_thread_create_sysdefault(unsigned int code_addr);
extern void task_thread_trytowakeup(k_process *process);
extern void task_process_create_sysdefault(k_thread *thread);

/*
 * Task Module Init
 */
void task_module_init(void)
{
	s_queue_process = NULL;
	g_current_thread = NULL;
	g_current_process = NULL;
	s_wait_terminate = NULL;
	mt_mutex_initset(&g_process_mutex);
	task_idmgr_init();
	task_procomm_share_init();
}

/*
 * 	select a process to run
 *@RETURN	ready running process
 */
static k_process *s_task_select_process(void)
{
	k_process *tmp = NULL;
	// we look for a maxume current priority process.
	tmp = s_queue_process;
	s_queue_process = tmp->next;
	if(s_queue_process)
	{
		// we must make sure the prev field is NULL if this is the first elements in list!
		s_queue_process->prev = NULL;
	}
	
	tmp->next = NULL;
	tmp->prev = NULL;
	return tmp;
}

/*
 *  select a thread to run.
 */
static k_thread *s_task_select_thread(k_process *process)
{
	k_thread *tmp = NULL;
	if(process == NULL)
	{
		// there must be many errors!serious!
		return NULL;
	}
	tmp = process->thread_runable;
	if(tmp == NULL)
	{
		// there may have some errors. this means that.may be we need to select another process.
		return NULL;
	}
	process->thread_runable = process->thread_runable->next;
	if(process->thread_runable)
		process->thread_runable->prev = NULL;
	
	tmp->next = NULL;
	tmp->prev = NULL;
	return tmp;
}

/*
 *  Process Priority Deal
 */
static s_task_process_priority_deal(k_process *process)
{
	if(process->cur_priority == TASK_PRIORITY_LOWES)
	{
		process->cur_priority = process->fix_priority;
		return;
	}
	process->cur_priority--;
}

/*
 *  Thread Priority Deal
 */
static s_task_thread_priority_deal(k_thread *thread)
{
	if(thread->cur_priority == TASK_PRIORITY_LOW )
	{
		thread->cur_priority = thread->fix_priority;
		return;
	}
	thread->cur_priority--;
}

/*
 *	task schedule with aotumatic select new thread
 * 		param 	: scheduled thread!if NULL it means that this thread has been terminated 
 *					we can't save context.just load next thread!
 * 		return	: void, we should avoid returnning value,because this is uncontrol when task schedule!
 * 		NOTE	: This function doesn't change the state of current thread, and also doesn't insert thread
 *					to any list.so, caller need to do this before call this function.
 *		WARNNING: We change the current priority only when schedule occured by timer!so, any other case schedule will
 *					don't change the priority!!!!!!!!!!!
 */
void task_schedule(k_thread *thread)
{
	k_process *old_process = NULL;
	
	/*	
	 * Comments:
	 * 	steps:
	 * 		1.check whether need and can schedule new thread or process
	 * 		=>> if g_process_mutex has been locked(depth != 0), we need to run the owner of mutex,otherwise 
	 *          there will be DEATH LOCK!
	 * 		2.if 1 allowed,"exchange" CPU context and MMU contex
	 * 		3.if 1 allowed,deal with the new process sigles
	 * 		4.make sure the next and prev fields of running thread is set to be NULL!
	 */
	// disable all interrupt. we need do this in critical section!
	// enter, when thread saved, CPSR is saved, so, thread is disable interrupt.when resume, we need to enable it.^_^
	comm_global_critical_section_enter();
	old_process = g_current_process;
	
	if(thread != NULL)
	{
		/* 
		 * 	if thread == NULL, means that current thread or process need to termianted.
		    if current thread or process has been marked terminate.
			we don't have save current thread Context of CPU.just load another
			thread of a different process to run.
		*/

		// save CPU context of current thread.
		unsigned int resume_addr = (unsigned int)(&&schedule);	// gcc use && get label address defined in current routine or subroutine
		TASK_CPU_CONTEXT_SAVE(g_current_thread, resume_addr);
	}
	
	// select a process. and select a thread to run!
	// infect, we don't allow this things!
	do
	{
		g_current_process = s_task_select_process();
		g_current_thread = s_task_select_thread(g_current_process);
		if(g_current_thread == NULL && g_current_process != NULL)
		{
			// link the the last!
			g_current_process->cur_priority = TASK_PRIORITY_LOWES;
			task_insert_process(g_current_process);
		}
	}while(g_current_thread==NULL);
	
	if(old_process != g_current_process)
	{
		// need to change MMU Base Table!!
		k_mmu_switch(g_current_process->mem.mmu.pt_phy_address);
	}
	
	g_p_cur_thread_sp = ((unsigned int)g_current_thread->sys_stack + TASK_THREAD_SYS_STACK_SIZE);
	
	// before leave critical section we check whether need to terminate thread or process
	task_thread_terminate_handler(g_current_process); // check current process terminated threads.
	task_process_terminate_handler();	// check process needed terminated.
	
	mt_mutex_overtime_handle(g_current_process);	// check thread of mutex waiting over time
	
	// shit...have made a big bug when lost here .... wast many time mine....shit ....
	g_current_thread->state = TASK_THREAD_STATE_RUNNING;

	comm_global_critical_section_leave();
	
	// resume thread CPU context!这个操作不需要在临界区中进行，只有保存现场需要在临界区中进行
	TASK_CPU_CONTEXT_RESUME(g_current_thread);
///*********************************************************************************************///	

	/* ################   WE CAN'T DO ANYTHING HERE, because it will never run here!It resumes to lable "schedule"! ########*/

///////////////////////////////////////////////////////////////////////////////////////////////////
schedule:	// ######## must resume from here, otherwise the following will never arrived! ##########
	// when thread resume running, it will run here at first time!

	// if we want do something appended.we'd better do it here.
	// here is out of critical section, so, will not block system.^_^
	
	// do more deals here, like signel check,deal  and so one...

	// check alam waitting thread, and try to wakeup them.^_^
	task_thread_trytowakeup(g_current_process);

	// end more deals.
}

/*
 *	send sigle to process
 * 		proc	process
 * 		type	sigle type
 * 		sigle	sigle value
 * NOTE:异步控制
 */
void task_process_sendsingle(k_process *process, unsigned int type, void *value)
{
}

/*
 *	process thread lock.
 * 		lock thread which just lock the thread in current process.
 */
void task_current_thread_lock(void)
{
	mt_mutex_lock(&g_current_process->thread_mutex, -1);
}

/*
 *	process thread unlock
 */
void task_current_thread_unlock(void)
{
	mt_mutex_unlock(&g_current_process->thread_mutex);
}

/*
 *  Task Timer Scheduler
 *  NOTE:	This Function Only Can Be Called By System Task Scheduler Timer, Any Other MUST DON'T Call this!
 */
void task_schedule_normal(void)
{
	// change the current priority of current process and current thread!	
	s_task_process_priority_deal(g_current_process);
	s_task_thread_priority_deal(g_current_thread);

	// second, insert the thread and process to the right list!
	task_insert_process(g_current_process);		// insert into s_queue_process.^_^
	task_thread_insert(g_current_thread, TASK_THREAD_STATE_RUNABLE); // insert into runable list.^_^

	// last, call schedule handle.^_^
	task_schedule(g_current_thread);
}

/*
 * create default task(process and it's main thread)
 */
void head_create_default_task(unsigned int *code_addr)
{
	k_thread *thread = task_thread_create_sysdefault((unsigned int)code_addr);
	task_process_create_sysdefault(thread);
}

